<?php

/**
 * trace.php
 * 
 * 跟踪(Trace)类
 * @author wang <wang@fvzone.com>
 * @copyright  Copyright (c) 2010 Fire vision Software Studio
 * @link http://www.fvzone.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: trace.php 1.0 2010-11-3 22:02:01Z wang $
 * @package core
 * @since 1.0
 */

class Trace extends Work
{
    protected static $_data;

	/**
	 * 删除项目文件的真实路径,用框架常量代替，预防被黑客利用
	 *
	 * @param   string 路径到跟踪类
	 * @return  string
	 */
	public static function path($file)
	{
		if (strpos($file, WORK_DIR) === 0)
		{
			$file = 'WORK_DIR'.DS.substr($file, strlen(WORK_DIR));
		}
		elseif (strpos($file, APP_DIR) === 0)
		{
			$file = 'APP_DIR'.DS.substr($file, strlen(APP_DIR));
		}
		elseif (strpos($file, COMPONENT_DIR) === 0)
		{
			$file = 'COMPONENT_DIR'.DS.substr($file, strlen(COMPONENT_DIR));
		}
		elseif (strpos($file, PUBLIC_DIR) === 0)
		{
			$file = 'PUBLIC_DIR'.DS.substr($file, strlen(PUBLIC_DIR));
		}

		return $file;
	}

	/**
	 * 返回错误文件的位置，并高亮错误行，并且加上行号
	 *
	 * @param   string   打开文件名
	 * @param   integer  行号，需要高亮的行
	 * @param   integer  截取源代码的行数
	 * @return  string   源文件部分代码
	 * @return  FALSE    文件不存在
	 */
	public static function source($file, $line_number, $padding = 4)
	{
		if (!$file or !is_readable($file))
		{
			// 继续将导致错误
			return false;
		}

		//打开文件，并设置开始的位置
		$file = fopen($file, 'r');
		$line = 0;

		//设置阅读范围
		$range = array('start' => $line_number - $padding, 'end' => $line_number + $padding);

		//设置行号的零填充量
		$format = '% '.strlen($range['end']).'d';

		$source = '';
		while (($row = fgets($file)) !== false)
		{
			//递增的行号
			if (++$line > $range['end'])
				break;

			if ($line >= $range['start'])
			{
				//转码保证输出安全
				$row = htmlspecialchars($row, ENT_NOQUOTES, 'utf-8');

				//Trim whitespace and sanitize the row
				$row = '<span class="number">'.sprintf($format, $line).'</span> '.$row;

				if ($line === $line_number)
				{
					//应用突出显示此行
					$row = '<span class="line highlight">'.$row.'</span>';
				}
				else
				{
					$row = '<span class="line">'.$row.'</span>';
				}

				//添加到捕获源
				$source .= $row;
			}
		}

		//关闭打开的文件
		fclose($file);

		return '<pre class="source"><code>'.$source.'</code></pre>';
	}

	/**
	 * 返回一个代表回溯中的每一步的HTML字符串数组
	 *
	 * @param   string 跟踪信息数组
	 * @return  string
	 */
	public static function back(array $trace = null)
	{
		//非标准的函数调用
		$statements = array('include', 'include_once', 'require', 'require_once');

		$output = array();
		foreach ($trace as $step)
		{
			if (!isset($step['function']))
			{
				//无效的跟踪步骤
				continue;
			}

			if (isset($step['file']) && isset($step['line']))
			{
				//这一步包含的源代码
				$source = self::source($step['file'], $step['line']);
			}

			if (isset($step['file']))
			{
				$file = $step['file'];

				if (isset($step['line']))
				{
					$line = $step['line'];
				}
			}

			// function()
			$function = $step['function'];

			if (in_array($step['function'], $statements))
			{
				if (empty($step['args']))
				{
					//没有参数
					$args = array();
				}
				else
				{
					// Sanitize the file path
					$args = array($step['args'][0]);
				}
			}
			elseif (isset($step['args']))
			{
				if ( ! function_exists($step['function']) || strpos($step['function'], '{closure}') !== false)
				{
					// Introspection on closures or language constructs in a stack trace is impossible
					$params = null;
				}
				else
				{
					if (isset($step['class']))
					{
						if (method_exists($step['class'], $step['function']))
						{
							$reflection = new ReflectionMethod($step['class'], $step['function']);
						}
						else
						{
							$reflection = new ReflectionMethod($step['class'], '__call');
						}
					}
					else
					{
						$reflection = new ReflectionFunction($step['function']);
					}

					//获取函数的参数
					$params = $reflection->getParameters();
				}

				$args = array();

				foreach ($step['args'] as $i => $arg)
				{
					if (isset($params[$i]))
					{
						//指定参数名称参数
						$args[$params[$i]->name] = $arg;
					}
					else
					{
						//分配数量的参数
						$args[$i] = $arg;
					}
				}
			}

			if (isset($step['class']))
			{
				// Class->method() or Class::method()
				$function = $step['class'].$step['type'].$step['function'];
			}

			$output[] = array(
				'function' => $function,
				'args'     => isset($args)   ? $args : null,
				'file'     => isset($file)   ? $file : null,
				'line'     => isset($line)   ? $line : null,
				'source'   => isset($source) ? $source : null,
			);
			unset($function, $args, $file, $line, $source);
		}
		return $output;
	}

    /**
     * Trace变量赋值
     * @access public
     * @param mixed $name
     * @param mixed $value
     */
    public static function set($title, $value = '')
    {
        if(is_array($title))
            self::$_data = array_merge(self::$_data, $title);
        else
            self::$_data[$title] = $value;
    }

    /**
     * 显示页面Trace信息
     * @access private
     */
    public static function show()
    {
        //显示页面Trace信息 读取Trace定义文件
        //定义格式 return array('当前页面'=>$_SERVER['PHP_SELF'],'通信协议'=>$_SERVER['SERVER_PROTOCOL'],...);

        $traceFile  = PUBLIC_DIR . 'config/trace.php';
        $_trace = is_file($traceFile)? include $traceFile : array();

        //系统默认显示信息
        
        self::set('当前页面',    $_SERVER['REQUEST_URI']);
        self::set('当前组件',    Request::component());
        self::set('模板缓存',    View::$compile_file);
        self::set('请求方法',    $_SERVER['REQUEST_METHOD']);
        self::set('通信协议',    $_SERVER['SERVER_PROTOCOL']);
        self::set('请求时间',    date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']));
        self::set('用户代理',    $_SERVER['HTTP_USER_AGENT']);
        self::set('会话ID'   ,   session_id());

        $log = Log::$log;
        self::set('日志记录', count($log) ? count($log).'条日志<br />'.implode('<br />', $log) : '无日志记录');
        
        $files  =  get_included_files();
        self::set('加载文件', count($files).str_replace("\n", '<br />',substr(substr(print_r($files, true), 7), 0, -2)));
        
        $_trace  = array_merge($_trace, self::$_data);
        
        $trace_tpl_file = PUBLIC_DIR . 'views/error/trace.tpl.php';
        include is_file($trace_tpl_file) ? $trace_tpl_file : WORK_DIR . 'assets/views/trace.tpl.php';
    }

    /**
     * 显示运行时间、数据库操作、缓存次数、内存使用信息
     * @access private
     * @return string
     */
    public static function time()
    {
        //显示运行时间
        $showTime = 'Process: '.Work::time('WORK_START_TIME','VIEW_END_TIME').'s ';
        
        //显示详细运行时间
        $showTime .= '( Loading:'.Work::time('WORK_START_TIME','WORK_INIT_TIME').'s Execute:'.Work::time('WORK_INIT_TIME','VIEW_START_TIME').'s Template:'.Work::time('VIEW_START_TIME','VIEW_END_TIME').'s )';

        //显示内存开销
        $start = array_sum(explode(' ', START_USE_MEMORY));
        $end = array_sum(explode(' ', memory_get_usage()));
        $showTime .= ' | Memory:'. number_format(($end - $start)/1024).' kb';

        return $showTime;
    }

}
